package net.m2technologies.open_arm.transaction;

import net.m2technologies.open_arm.ArmErrorHandler;
import net.m2technologies.open_arm.transport.TransportMediatorBroker;
import net.m2technologies.open_arm.utilities.OpenArmUtilities;
import net.m2technologies.open_arm.utilities.guid.RandomGUID;
import org.opengroup.arm40.transaction.*;

import java.util.*;

/**
 * Copyright 2005 Mark Masterson<br> <br> Licensed under the Apache License, Version 2.0 (the "License");<br> you may
 * not use this file except in compliance with the License.<br> You may obtain a copy of the License at<br> <br>
 * http://www.apache.org/licenses/LICENSE-2.0<br> <br> Unless required by applicable law or agreed to in writing,
 * software<br> distributed under the License is distributed on an "AS IS" BASIS,<br> WITHOUT WARRANTIES OR CONDITIONS
 * OF ANY KIND, either express or implied.<br> See the License for the specific language governing permissions and<br>
 * limitations under the License.<br>
 * <p/>
 * <p>Description: </p>
 *
 * @author Mark Masterson
 * @version 0.010
 */
public class ArmTransactionFactoryImpl extends ArmInterfaceImpl implements ArmTransactionFactory,
                                                                           ArmErrorHandler,
                                                                           OpenArmUtilities {

    private final Collection registeredErrorCallbackListeners = new HashSet();
    private final TransportMediatorBroker transportMediatorBroker;

    public ArmTransactionFactoryImpl(final TransportMediatorBroker transportMediatorBroker) {
        super(null);
        this.transportMediatorBroker = transportMediatorBroker;
    }

    public TransportMediatorBroker getTransportMediatorBroker() {
        return this.transportMediatorBroker;
    }

    public synchronized RandomGUID getGuid() {
        return new RandomGUID();
    }

    public RandomGUID getGuid(final byte[] idBytes) {
        return new RandomGUID(idBytes);
    }

    public ArmApplication newArmApplication(final ArmApplicationDefinition definition,
                                            final String group,
                                            final String instance,
                                            final String[] contextValues) {
        return ((ArmApplicationDefinitionImpl) definition).getApplication(contextValues, group, instance, this);
    }

    public ArmApplicationDefinition newArmApplicationDefinition(final String name,
                                                                final ArmIdentityProperties identityProperties,
                                                                final ArmID id) {
        return new ArmApplicationDefinitionImpl(id, identityProperties, name, this);
    }

    public ArmCorrelator newArmCorrelator(final byte[] corrBytes) {
        return new ArmCorrelatorImpl(new RandomGUID(corrBytes), this);
    }

    public ArmCorrelator newArmCorrelator(final byte[] corrBytes, final int offset) {
        return newArmCorrelator(corrBytes);
    }

    public ArmID newArmID(final byte[] idBytes) {
        return new ArmIdImpl(this);
    }

    public ArmID newArmID(final byte[] idBytes, final int offset) {
        return new ArmIdImpl(this);
    }

    public ArmIdentityProperties newArmIdentityProperties(final String[] identityNames,
                                                          final String[] identityValues,
                                                          final String[] contextNames) {
        handleIdentityPropertiesPreconditions(identityNames, identityValues, contextNames);
        final Map identityPropsMap = buildIdentityPropertiesMap(identityNames, identityValues);
        final Map contextPropsMap = buildContextPropertiesMap(contextNames);
        return new ArmIdentityPropertiesImpl(identityPropsMap, contextPropsMap, this);
    }

    public ArmIdentityPropertiesTransaction newArmIdentityPropertiesTransaction(final String[] identityNames,
                                                                                final String[] identityValues,
                                                                                final String[] contextNames,
                                                                                final String uriValue) {
        handleIdentityPropertiesPreconditions(identityNames, identityValues, contextNames);
        final Map identityPropsMap = buildIdentityPropertiesMap(identityNames, identityValues);
        final Map contextPropsMap = buildContextPropertiesMap(contextNames);
        return new ArmIdentityPropertiesTransactionImpl(identityPropsMap, contextPropsMap, uriValue, this);
    }

    private Map buildIdentityPropertiesMap(final String[] identityNames, final String[] identityValues) {
        final Map idProps = new HashMap();
        for (int i = 0; i < identityNames.length; i++) {
            final IdentityProperty id = new IdentityProperty(identityNames[i], identityValues[i]);
            idProps.put(new Integer(i), id);
        }
        return idProps;
    }

    private Map buildContextPropertiesMap(final String[] contextNames) {
        final Map contextProps = new HashMap();
        for (int i = 0; i < contextNames.length; i++) {
            final IdentityProperty id = new IdentityProperty(contextNames[i], null);
            contextProps.put(new Integer(i), id);
        }
        return contextProps;
    }

    private void handleIdentityPropertiesPreconditions(final String[] identityNames,
                                                       final String[] identityValues,
                                                       final String[] contextNames) {
        if (null == identityNames || null == identityValues || null == contextNames) {
            throw new IllegalArgumentException("Parameters may not be null!");
        }
        if (identityNames.length != identityValues.length) {
            throw new IllegalArgumentException("'identityNames' and 'identityValues' must be the same length!");
        }
    }

    public ArmTransaction newArmTransaction(final ArmApplication app,
                                            final ArmTransactionDefinition definition) {
        return ((ArmTransactionDefinitionImpl) definition).getTransaction(this,
                                                                          app,
                                                                          new String[]{});
    }

    public ArmTransactionDefinition newArmTransactionDefinition(final ArmApplicationDefinition app,
                                                                final String name,
                                                                final ArmIdentityPropertiesTransaction identityProperties,
                                                                final ArmID id) {
        return ((ArmApplicationDefinitionImpl) app).getTransactionDefinition(name, this);
    }

    public ArmUser newArmUser(final String name, final ArmID id) {
        return new ArmUserImpl(id, name, this);
    }

    public boolean setErrorCallback(final ArmErrorCallback errorCallback) {
        this.registeredErrorCallbackListeners.add(errorCallback);
        return true;
    }

    public void fireErrorEvent(final ArmInterface originatingOject, final String methodName) {
        for (final Iterator errorListeners = this.registeredErrorCallbackListeners.iterator();
             errorListeners.hasNext();) {
            final ArmErrorCallback errorCallback = (ArmErrorCallback) errorListeners.next();
            errorCallback.errorCodeSet(originatingOject, originatingOject.getClass().getName(), methodName);
        }
    }
}
